// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// Package cmdflag处理多个go工具通用的标志处理。
package cmdflag

import (
	"errors"
	"flag"
	"fmt"
	"strings"
)

// go命令（如test）的标志处理部分很大，会分散注意力。
// 我们无法使用标准标志包，因为来自
// 我们的命令行是为我们准备的，有些是为我们正在运行的二进制文件准备的，
// 有些是两者都适用的。

// ErrFlagTerminator表示可分辨标记“-”，它导致
// 将所有后续参数视为非标志的标志包。
var ErrFlagTerminator = errors.New("flag terminator")

// FlagNotDefinedError表示不对应的类似于标志的参数
// 指向旗标集中的任何已注册旗标。
type FlagNotDefinedError struct {
	RawArg   string // 原始参数，如--foo或-foo=value
	Name     string
	HasValue bool   // 这是-foo=value还是--foo=value形式？
	Value    string // 仅当HasValue为true时提供
}

func (e FlagNotDefinedError) Error() string {
	return fmt.Sprintf("flag provided but not defined: -%s", e.Name)
}

// 非标记错误表示不是语法有效标志的参数。
type NonFlagError struct {
	RawArg string
}

func (e NonFlagError) Error() string {
	return fmt.Sprintf("not a flag: %q", e.RawArg)
}

// ParseOne查看给定标志集中是否存在args[0]，如果存在，
// 设置其值并返回标志以及剩余（未使用的）参数。
// None
// ParseOne始终返回非nil标志或非nil错误，
// 并且始终使用至少一个参数（即使出现错误）。
// None
// 与（*flag.FlagSet）.Parse不同，ParseOne不会记录自己的错误。
func ParseOne(fs *flag.FlagSet, args []string) (f *flag.Flag, remainingArgs []string, err error) {
	// 此函数松散地派生自（*flag.FlagSet）.parseOne。

	raw, args := args[0], args[1:]
	arg := raw
	if strings.HasPrefix(arg, "--") {
		if arg == "--" {
			return nil, args, ErrFlagTerminator
		}
		arg = arg[1:] // 减二为一
	}

	switch arg {
	case "-?", "-h", "-help":
		return nil, args, flag.ErrHelp
	}
	if len(arg) < 2 || arg[0] != '-' || arg[1] == '-' || arg[1] == '=' {
		return nil, args, NonFlagError{RawArg: raw}
	}

	name := arg[1:]
	hasValue := false
	value := ""
	if i := strings.Index(name, "="); i >= 0 {
		value = name[i+1:]
		hasValue = true
		name = name[0:i]
	}

	f = fs.Lookup(name)
	if f == nil {
		return nil, args, FlagNotDefinedError{
			RawArg:   raw,
			Name:     name,
			HasValue: hasValue,
			Value:    value,
		}
	}

	// 使用下面的fs.Set而不是f.Value.Set，以便任何后续调用
	// fs.Visit将正确访问已设置的标志。

	failf := func(format string, a ...interface{}) (*flag.Flag, []string, error) {
		return f, args, fmt.Errorf(format, a...)
	}

	if fv, ok := f.Value.(boolFlag); ok && fv.IsBoolFlag() { // 特殊情况：不需要arg
		if hasValue {
			if err := fs.Set(name, value); err != nil {
				return failf("invalid boolean value %q for -%s: %v", value, name, err)
			}
		} else {
			if err := fs.Set(name, "true"); err != nil {
				return failf("invalid boolean flag %s: %v", name, err)
			}
		}
	} else {
		// 它必须有一个值，这可能是下一个参数。
		if !hasValue && len(args) > 0 {
			// 值是下一个参数
			hasValue = true
			value, args = args[0], args[1:]
		}
		if !hasValue {
			return failf("flag needs an argument: -%s", name)
		}
		if err := fs.Set(name, value); err != nil {
			return failf("invalid value %q for flag -%s: %v", value, name, err)
		}
	}

	return f, args, nil
}

type boolFlag interface {
	IsBoolFlag() bool
}
